import 'dart:async';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:stack_trace/stack_trace.dart';
import 'extension/datetime.dart';

enum LogLevel {
  off,
  fatal,
  error,
  warn,
  debug,
  verbose,
}

void logv(String tag, Object object) {
  JmLog.v(tag, object);
}

void logd(String tag, Object? object, {bool stack = false}) {
  JmLog.d(tag, object, stack: stack);
}

void logw(String tag, Object object) {
  JmLog.w(tag, object);
}

void loge(String tag, Object object, {StackTrace? stackTrace}) {
  JmLog.e(tag, object, stackTrace: stackTrace);
}

void logf(String tag, Object object) {
  JmLog.e(tag, object);
}

String nowTimeStr() {
  DateTime now = DateTime.now();
  return now.toLogShort;
}

class JmLog {
  static final List<JmLogDesc> logList = [];
  static final StreamController<bool> _logStreamController = StreamController<bool>.broadcast();
  static LogLevel logLevel = LogLevel.verbose;

  static Stream<bool> get logStream => _logStreamController.stream;
  static StreamSink get _logSink => _logStreamController.sink;

  static void clear() {
    logList.clear();
    _logSink.add(true);
  }

  static Future<File> saveLog({required String filePath}) async {
    final file = File(filePath);
    final logBuffer = StringBuffer(); // 使用 StringBuffer 来减少 I/O 操作次数
    for (var item in logList) {
      String oneLog = "";

      switch (item.level) {
        case LogLevel.off:
          continue; // off 级别的日志可以直接跳过
        case LogLevel.fatal:
        case LogLevel.error:
          oneLog = "\n-------------ERROR-------------\n${item.time.toLogFull} ${item.msg}\n-------------------------------\n\n";
          break;
        case LogLevel.warn:
          oneLog = "▷ ${item.time.toLogFull} ${item.msg}\n";
          break;
        case LogLevel.debug:
        case LogLevel.verbose:
          oneLog = "${item.time.toLogFull} ${item.msg}\n";
          break;
      }
      logBuffer.write(oneLog); // 将日志写入缓存
    }

    await file.writeAsString(logBuffer.toString(), mode: FileMode.append); // 一次性写入文件
    return file;
  }

  static void d(String tag, Object? object, {bool stack = false}) {
    final logDesc = "[$tag] $object";

    logList.add(JmLogDesc(LogLevel.debug, DateTime.now(), logDesc));
    _logSink.add(true);

    if (!kDebugMode) {
      return;
    }

    if (logLevel.index < LogLevel.debug.index) {
      return;
    }

    final timeStr = nowTimeStr();
    String str;

    if (stack) {
      String trace = Trace.current().frames[2].location;
      str = "$timeStr [$tag] $trace $object";
    } else {
      str = "$timeStr $logDesc";
    }

    // if (Platform.isAndroid) {
    //   print("\x1B[34m$str\x1B[0m");
    // } else {
    print(str);
    //}
  }

  static void v(String tag, Object object) {
    final logDesc = "[$tag] $object";
    logList.add(JmLogDesc(LogLevel.verbose, DateTime.now(), logDesc));
    _logSink.add(true);

    if (!kDebugMode) {
      return;
    }

    if (logLevel.index < LogLevel.verbose.index) {
      return;
    }

    final timeStr = nowTimeStr();

    final str = "$timeStr $logDesc";

    // if (Platform.isAndroid) {
    //   print("\x1B[32m$str\x1B[0m");
    // } else {
    print(str);
    //}
  }

  static void w(String tag, Object object) {
    final logDesc = "[$tag] $object";
    logList.add(JmLogDesc(LogLevel.warn, DateTime.now(), logDesc));
    _logSink.add(true);

    if (!kDebugMode) {
      return;
    }

    if (logLevel.index < LogLevel.warn.index) {
      return;
    }

    String timeStr = "";
    timeStr = nowTimeStr();

    final str = "▷ $timeStr $logDesc";

    // if (Platform.isAndroid) {
    //   print("\x1B[33m$str\x1B[0m");
    // } else {
    print(str);
    //}
  }

  static void e(String tag, Object object, {bool time = true, StackTrace? stackTrace}) {
    late String trace;
    if (stackTrace != null) {
      trace = stackTrace.toString();
    } else {
      trace = Trace.current().frames[2].location;
      //trace = Trace.current().frames.toString();
    }

    final logDesc = "[$tag] $trace\n$object";
    logList.add(JmLogDesc(LogLevel.error, DateTime.now(), logDesc));
    _logSink.add(true);

    //Apm.captureException("[$tag] $object", stackTrace: trace);

    if (!kDebugMode) {
      return;
    }

    if (logLevel.index < LogLevel.error.index) {
      return;
    }

    String timeStr = "";

    if (time) {
      timeStr = nowTimeStr();
    }

    final str = "$timeStr ▶ $logDesc";

    // if (Platform.isAndroid) {
    //   print("\x1B[31m$str\x1B[0m");
    // } else {
    print(str);
    //}
  }
}

class JmLogDesc {
  final LogLevel level;
  final DateTime time;
  final String msg;
  JmLogDesc(this.level, this.time, this.msg);
}
